"""
@author: badmonkey
@software: PyCharm
@file: semantic.py
@time: 2021/4/18 下午6:47
"""

# 导入必要的模块
from Parser.AST import *  # 导入抽象语法树（AST）相关类和方法
from Parser.LL1 import *  # 导入 LL(1) 语法分析器相关功能
from Lexer.scanner import *  # 导入词法分析器相关功能
from Analyzer.symbol import *  # 导入符号表相关类和方法


# 定义状态类，用于表示语义分析的不同阶段
class myState:
    START = "start"  # 开始状态
    TYPEDEC = "typedec"  # 类型声明状态
    VARDEC = "vardec"  # 变量声明状态
    PRODEC = "prodec"  # 过程声明状态
    DONE = "done"  # 完成状态


# 定义语义错误类，包含各种可能的语义错误类型及其描述
class semanticError(object):
    duplicateDefine = "duplicate defined"  # 重复定义错误
    unDefinedVar = "undefined variable"  # 未定义变量错误
    unDefinedType = "undefined type"  # 未定义类型错误
    undefinedField = "undefined field"  # 未定义字段错误
    invalidAssignLeft = "invalid assignment %s is not variable"  # 赋值左侧无效（不是变量）
    invalidAssignRight = "invalid assignment %s is not %s type"  # 赋值右侧类型不匹配
    paramTypeError = "procedure param type do not match expect %s got %s"  # 过程参数类型不匹配
    paramNumError = "procedure param number do not match expect %d params got %d params"  # 过程参数数量不匹配
    procedureCallError = "can not be called"  # 无法调用的过程错误
    boolError = "conditon must be an boolean value"  # 条件必须是布尔值错误
    typeMatchError = "Uncompatable type  expect %s got %s"  # 类型不兼容错误
    arrayDefineError = "invalid array define"  # 无效数组定义错误
    kindMatchError = "unexpect type expect %s got %s"  # 类型不匹配错误


# 定义语义分析器类
class Analyzer(object):

    # 初始化方法
    def __init__(self, tokens, root):
        self.index = 0  # 当前处理的 token 索引
        self.tokens = tokens  # 词法分析生成的 token 列表
        self.root = root  # 语法分析生成的 AST 根节点
        self.symTable = SymbolTable()  # 当前作用域的符号表
        self.state = myState.START  # 当前状态，初始为 START
        self.current = root  # 当前处理的 AST 节点
        self.error = False  # 是否发生错误
        self.scope = [self.symTable]  # 作用域栈，初始包含全局符号表
        self.errorMessage = ""  # 错误信息
        self.currentVarType = ""  # 当前变量的类型
        self.currentVarKind = ""  # 当前变量的种类
        self.currentExp = ""  # 当前表达式
        self.currentVarValue = ""  # 当前变量的值
        self.currentVar = None  # 当前处理的变量符号
        self.printerror = True  # 是否打印错误信息
        self.symLevel = 0  # 当前符号表层级
        self.errorLine = 0  # 发生错误的行号

    # 返回符号表中符号数量
    def __len__(self):
        return len(self.symTable)

    # 打印符号表内容
    def __repr__(self):
        print(self.symTable)
        return ""

    # 更新 token 索引
    def updateIndex(self):
        # 如果当前节点不是空节点（ε）且有 token 值，则索引加 1
        if self.current.getTokenVal() != "ε" and (
                self.current.getTokenVal() or self.current.getTokenVal() == 0
        ):
            self.index += 1
            # 调试用：打印当前索引和 token 值
            # print(self.index, self.tokens[self.index-1][0], self.current.getTokenVal())

    # 步进到下一个 AST 节点
    def step(self):
        if self.current.isEmpty():  # 如果当前节点为空
            self.current = self.current.step()  # 移动到兄弟节点
        else:
            self.current = self.current.firstChild()  # 移动到第一个子节点
        self.updateIndex()  # 更新 token 索引

    # 步进到指定类型的 token
    def stepInto(self, tokenType):
        while not self.current.isTokenType(tokenType):  # 循环直到找到指定类型
            self.step()

    # 获取前一个 token
    def preToken(self):
        if self.index - 2 > 0:
            return self.tokens[self.index - 2][1]  # 返回前一个 token 的值
        else:
            return None

    # 获取下一个 token
    def nextToken(self):
        if self.index < len(self.tokens):
            return self.tokens[self.index][1]  # 返回下一个 token 的值
        else:
            return None

    # 获取当前行号
    def currenLine(self):
        if self.index - 1 > 0:
            return self.tokens[self.index - 1][-1]  # 返回当前 token 的行号
        else:
            return None

    # 获取当前 token
    def currentToken(self):
        if self.index - 1 > 0:
            return self.tokens[self.index - 1][1]  # 返回当前 token 的值
        else:
            return None

    # 打印错误信息
    def printError(self, var=None):
        # 如果不打印错误或当前行号与上一次错误行号相同，则跳过
        if not self.printerror and self.tokens[self.index - 1][-1] == self.errorLine:
            pass
        else:
            if var != None:  # 如果提供了变量信息
                print(
                    "Error in line: %d\tDetail: %s %s %s"
                    % (
                        self.tokens[self.index - 1][-1],  # 行号
                        var.decKind,  # 变量种类
                        var.name,  # 变量名
                        self.errorMessage,  # 错误信息
                    )
                )
            else:  # 没有变量信息
                print(
                    "Error in line: %d\tDetail: %s %s %s"
                    % (
                        self.tokens[self.index - 1][-1],  # 行号
                        self.tokens[self.index - 1][0],  # token 类型
                        self.currentToken(),  # token 值
                        self.errorMessage,  # 错误信息
                    )
                )

    # 检查赋值语句的类型兼容性
    def assignTypeCheck(self, leftType, rightType):
        if leftType == "INTEGER":  # 如果左侧是 INTEGER 类型
            return rightType == "INTEGER" or rightType == "INTC"  # 右侧可以是 INTEGER 或 INTC
        else:
            return leftType == rightType  # 其他情况要求类型完全相同

    # 主分析方法
    def analyze(self):
        self.current = self.root.firstChild()  # 从 AST 根节点的第一个子节点开始
        self.programHead()  # 分析程序头部
        self.declarePart()  # 分析声明部分
        self.programBody()  # 分析程序体

    # 分析程序头部
    def programHead(self):
        self.stepInto("ProgramName")  # 步进到程序名节点
        self.stepInto("ID")  # 步进到标识符（程序名）

    # 分析声明部分
    def declarePart(self):
        self.stepInto("DeclarePart")  # 步进到声明部分
        self.typeDec()  # 分析类型声明
        self.varDec()  # 分析变量声明
        self.procDec()  # 分析过程声明
        # 调试用：打印作用域栈
        # print(self.scope)

    # 分析程序体
    def programBody(self):
        self.stepInto("BEGIN")  # 步进到 BEGIN 节点
        self.stmList()  # 分析语句列表
        self.stepInto("END")  # 步进到 END 节点

    # 分析类型声明
    def typeDec(self):
        self.stepInto("TypeDec")  # 步进到类型声明节点
        self.step()  # 前进一步
        if self.current.isTokenType("ε"):  # 如果是空节点（无类型声明）
            return
        else:
            self.stepInto("TypeDecList")  # 步进到类型声明列表
            while True:
                # 如果到达 TypeDecMore 且其子节点为空，则退出循环
                if self.current.isTokenType("TypeDecMore") and self.current.firstChild().isTokenType("ε"):
                    break
                else:
                    self.stepInto("TypeId")  # 步进到类型标识符
                    self.stepInto("ID")  # 步进到具体标识符
                    sym = Symbol(kind="typeDec", name=self.current.getTokenVal())  # 创建类型声明符号
                    typeName = self.typeName()  # 获取类型名
                    if typeName != None:
                        sym.typePtr = typeName  # 设置类型指针
                        if sym.name not in self.symTable:  # 如果符号表中不存在该类型名
                            self.symTable.add(sym)  # 添加到符号表
                        else:  # 重复定义错误
                            self.error = True
                            self.errorMessage = semanticError.duplicateDefine % (sym.name)
                            self.printError()
                    self.stepInto("TypeDecMore")  # 步进到下一个类型声明

    # 分析变量声明
    def varDec(self):
        self.stepInto("VarDec")  # 步进到变量声明节点
        self.step()  # 前进一步
        if self.current.isTokenType("ε"):  # 如果是空节点（无变量声明）
            return
        else:
            self.stepInto("VarDecList")  # 步进到变量声明列表
            while True:
                # 如果到达 VarDecMore 且其子节点为空，则退出循环
                if self.current.isTokenType("VarDecMore") and self.current.firstChild().isTokenType("ε"):
                    break
                else:
                    typeName = self.typeName()  # 获取变量类型
                    self.stepInto("VarIdList")  # 步进到变量标识符列表
                    while True:
                        # 如果到达 VarIdMore 且其子节点为空，则退出循环
                        if self.current.isTokenType("VarIdMore") and self.current.firstChild().isTokenType("ε"):
                            break
                        else:
                            sym = Symbol(kind="varDec", type=typeName)  # 创建变量声明符号
                            self.stepInto("ID")  # 步进到具体标识符
                            sym.name = self.current.getTokenVal()  # 设置变量名
                            if sym.name in self.symTable:  # 如果变量名已存在
                                self.error = True
                                self.errorMessage = semanticError.duplicateDefine  # 重复定义错误
                                self.printError()
                            else:
                                self.symTable.add(sym)  # 添加到符号表
                            self.stepInto("VarIdMore")  # 步进到下一个变量标识符
                    self.stepInto("VarDecMore")  # 步进到下一个变量声明

    # 分析过程声明
    def procDec(self):
        curSymTab = self.symTable  # 保存当前符号表
        self.stepInto("ProcDec")  # 步进到过程声明节点
        self.step()  # 前进一步
        if self.current.isTokenType("ε"):  # 如果是空节点（无过程声明）
            return
        else:
            while True:
                # 如果到达 ProcDecMore 且其子节点为空，则退出循环
                if self.current.isTokenType("ProcDecMore") and self.current.firstChild().isTokenType("ε"):
                    break
                else:
                    self.stepInto("ProcName")  # 步进到过程名节点
                    self.step()  # 前进一步
                    procName = self.current.getTokenVal()  # 获取过程名
                    proc = Symbol(kind="procDec", name=procName, param=SymbolTable())  # 创建过程符号
                    symTable = SymbolTable()  # 创建新的符号表用于过程
                    symTable.add(proc)  # 将过程符号添加到新符号表
                    procError = False
                    if procName in self.symTable:  # 如果过程名已存在
                        procError = True
                        self.error = True
                        self.errorMessage = semanticError.duplicateDefine  # 重复定义错误
                        self.printError()
                    else:
                        self.symTable.add(proc)  # 添加到当前符号表
                        self.scope.append(symTable)  # 将新符号表加入作用域栈
                        self.symTable = self.scope[-1]  # 更新当前符号表为新符号表
                    self.stepInto("ParamList")  # 步进到参数列表
                    self.step()  # 前进一步
                    if self.current.isTokenType("ε"):  # 如果参数列表为空
                        pass
                    else:
                        while True:
                            # 如果到达 ParamMore 且其子节点为空，则退出循环
                            if self.current.isTokenType("ParamMore") and self.current.firstChild().isTokenType("ε"):
                                break
                            else:
                                self.stepInto("Param")  # 步进到参数节点
                                typeName = self.typeName()  # 获取参数类型
                                typeError = False
                                if typeName == None:  # 如果类型未定义
                                    typeError = True
                                self.stepInto("FormList")  # 步进到形式参数列表
                                while True:
                                    # 如果到达 FidMore 且其子节点为空，则退出循环
                                    if self.current.isTokenType("FidMore") and self.current.firstChild().isTokenType(
                                            "ε"):
                                        break
                                    else:
                                        self.stepInto("ID")  # 步进到参数标识符
                                        param = Symbol(
                                            kind="varDec",
                                            name=self.current.getTokenVal(),
                                            type=typeName,
                                        )  # 创建参数符号
                                        proc.param.add(param)  # 添加到过程参数表
                                        if not procError and not typeError:  # 如果过程和类型无错误
                                            if param.name not in self.symTable:  # 参数名未重复
                                                self.symTable.add(param)  # 添加到符号表
                                            else:  # 参数名重复
                                                self.error = True
                                                self.errorMessage = semanticError.duplicateDefine
                                                self.printError()
                                        self.stepInto("FidMore")  # 步进到下一个参数
                                self.stepInto("ParamMore")  # 步进到下一个参数声明
                    self.stepInto("ProcDecPart")  # 步进到过程声明部分
                    self.declarePart()  # 分析过程内的声明
                    self.stepInto("ProcBody")  # 步进到过程体
                    self.programBody()  # 分析过程体
                    self.symTable = curSymTab  # 恢复当前符号表
                    self.stepInto("ProcDecMore")  # 步进到下一个过程声明

    # 分析语句列表
    def stmList(self):
        self.stepInto("Stm")  # 步进到语句节点
        while True:
            # 如果到达 StmMore 且其子节点为空，则退出循环
            if self.current.isTokenType("StmMore") and self.current.firstChild().isTokenType("ε"):
                break
            else:
                self.stepInto("Stm")  # 步进到具体语句
                curStm = self.current.firstChild().getTokenType()  # 获取语句类型
                if curStm == "ConditionalStm":  # 条件语句
                    self.conditionalStm()
                elif curStm == "LoopStm":  # 循环语句
                    self.loopStm()
                elif curStm == "InputStm":  # 输入语句
                    self.inputStm()
                elif curStm == "OutputStm":  # 输出语句
                    self.outputStm()
                elif curStm == "ReturnStm":  # 返回语句
                    self.returnStm()
                elif curStm == "ID":  # 标识符（赋值或过程调用）
                    self.stepInto("ID")  # 步进到标识符
                    idName = self.current.getTokenVal()  # 获取标识符名
                    self.stepInto("AssCall")  # 步进到赋值或调用节点
                    self.step()  # 前进一步
                    decision = self.current.getTokenType()  # 判断是赋值还是调用
                    varError = True
                    var = None
                    # 从作用域栈逆序查找变量
                    for symTable in self.scope[::-1]:
                        if idName in symTable:
                            varError = False
                            var = symTable.get(idName)
                            break
                    if varError:  # 如果变量未定义
                        self.error = True
                        self.errorMessage = semanticError.unDefinedVar
                        self.printError()
                    if decision == "AssignmentRest":  # 赋值语句
                        varType = None
                        self.stepInto("VariMore")  # 步进到变量修饰节点
                        self.step()  # 前进一步
                        choice = self.current.getTokenType()  # 获取修饰类型
                        if choice == "ε":  # 无修饰
                            if not varError:
                                varType = var.typePtr.type  # 获取变量类型
                        elif choice == "[":  # 数组索引
                            indType, indVal = self.expresion()  # 分析索引表达式
                            if indType not in ["INTEGER", "INTC"]:  # 索引类型必须是整数
                                self.error = True
                                self.errorMessage = semanticError.typeMatchError % ("INTC", indType)
                                self.printError()
                            else:
                                varType = var.typePtr.element.type  # 获取数组元素类型
                        elif choice == ".":  # 结构体字段
                            self.stepInto("FieldVar")  # 步进到字段变量
                            self.stepInto("ID")  # 步进到字段名
                            fieldName = self.current.getTokenVal()  # 获取字段名
                            if var.typePtr.type != "recordType":  # 如果不是结构体类型
                                self.error = True
                                self.errorMessage = semanticError.typeMatchError % ("recordType", var.typePtr.type)
                                self.printError(var)
                            else:
                                if fieldName not in var.typePtr.fieldList:  # 字段未定义
                                    self.error = True
                                    self.errorMessage = semanticError.undefinedField
                                    self.printError()
                                else:
                                    field = var.typePtr.fieldList.get(fieldName)  # 获取字段符号
                                    self.stepInto("FieldVarMore")  # 步进到字段修饰
                                    self.step()  # 前进一步
                                    if self.current.isTokenType("ε"):  # 无修饰
                                        varType = field.typePtr.type  # 获取字段类型
                                    elif self.current.isTokenType("["):  # 数组索引
                                        indType, indVal = self.expresion()  # 分析索引表达式
                                        if indType not in ["INTEGER", "INTC"]:  # 索引类型必须是整数
                                            self.error = True
                                            self.errorMessage = semanticError.typeMatchError % ("INTC", indType)
                                            self.printError()
                                        else:
                                            varType = field.typePtr.element.type  # 获取数组元素类型
                        self.stepInto(":=")  # 步进到赋值符号
                        rightType, rigthVal = self.expresion()  # 分析右侧表达式
                        if varType and not varError:  # 如果类型有效且变量无错误
                            if not self.assignTypeCheck(varType, rightType):  # 检查类型兼容性
                                self.error = True
                                self.errorMessage = semanticError.typeMatchError % (varType, rightType)
                                self.printError()
                    elif decision == "CallStmRest":  # 过程调用
                        self.stepInto("ActParamList")  # 步进到实际参数列表
                        actParamList = []  # 存储实际参数类型
                        while True:
                            # 如果到达 ActParamMore 或 ActParamList 且子节点为空，则退出循环
                            if self.current.isTokenType("ActParamMore") and self.current.firstChild().isTokenType("ε"):
                                break
                            elif self.current.isTokenType("ActParamList") and self.current.firstChild().isTokenType(
                                    "ε"):
                                break
                            else:
                                expType, expVal = self.expresion()  # 分析参数表达式
                                actParamList.append(expType)  # 添加参数类型
                                self.stepInto("ActParamMore")  # 步进到下一个参数
                        actParamLen = len(actParamList)  # 实际参数数量
                        paramList = SymbolTable()  # 初始化形式参数表
                        if not varError:  # 如果标识符存在
                            if var.decKind != "procDec":  # 如果不是过程
                                self.error = True
                                self.errorMessage = semanticError.procedureCallError
                                self.printError(var)
                            else:  # 是过程
                                paramList = var.param  # 获取形式参数表
                                paramLen = len(paramList)  # 形式参数数量
                                if paramLen != actParamLen:  # 参数数量不匹配
                                    self.error = True
                                    self.errorMessage = semanticError.paramNumError % (paramLen, actParamLen)
                                    self.printError(var)
                                else:  # 参数数量匹配
                                    for i in range(paramLen):  # 检查每个参数类型
                                        if paramList[i].typePtr == None:
                                            continue
                                        expect = paramList[i].typePtr.type  # 期望类型
                                        got = actParamList[i]  # 实际类型
                                        if not self.assignTypeCheck(expect, got):  # 类型不匹配
                                            self.error = True
                                            self.errorMessage = semanticError.typeMatchError % (expect, got)
                                            self.printError()
                self.stepInto("StmMore")  # 步进到下一条语句

    # 分析类型名
    def typeName(self):
        self.stepInto("TypeName")  # 步进到类型名节点
        choice = self.current.firstChild().getTokenType()  # 获取类型种类
        self.stepInto(choice)  # 步进到具体类型
        if choice == "ID":  # 用户定义类型
            idName = self.current.getTokenVal()  # 获取类型名
            if idName in self.symTable:  # 如果类型已定义
                sym = self.symTable.get(idName)
                return sym.typePtr  # 返回类型指针
            else:  # 类型未定义
                self.error = True
                self.errorMessage = semanticError.unDefinedType
                self.printError()
                self.printerror = False
                return None
        elif choice == "BaseType":  # 基本类型
            return self.baseType()
        elif choice == "StructureType":  # 结构类型
            structType = self.current.firstChild().getTokenType()  # 获取结构类型
            self.stepInto(structType)  # 步进到具体结构类型
            if structType == "ArrayType":  # 数组类型
                return self.arrayType()
            elif structType == "RecType":  # 记录类型
                fieldList = SymbolTable()  # 创建字段符号表
                self.stepInto("RECORD")  # 步进到 RECORD 节点
                while True:
                    # 如果到达 FieldDecMore 且子节点为空，则退出循环
                    if self.current.isTokenType("FieldDecMore") and self.current.firstChild().isTokenType("ε"):
                        break
                    else:
                        self.stepInto("FieldDecList")  # 步进到字段声明列表
                        fieldTypeKind = self.current.firstChild().getTokenType()  # 获取字段类型种类
                        fieldType = None
                        if fieldTypeKind == "BaseType":  # 基本类型
                            fieldType = self.baseType()
                        elif fieldTypeKind == "ArrayType":  # 数组类型
                            fieldType = self.arrayType()
                        while True:
                            # 如果到达 IdMore 且子节点为空，则退出循环
                            if self.current.isTokenType("IdMore") and self.current.firstChild().isTokenType("ε"):
                                break
                            else:
                                self.stepInto("IdList")  # 步进到标识符列表
                                self.stepInto("ID")  # 步进到具体标识符
                                sym = Symbol(
                                    kind="varDec",
                                    type=fieldType,
                                    name=self.current.getTokenVal(),
                                )  # 创建字段符号
                                if sym.name in fieldList:  # 字段名重复
                                    self.error = True
                                    self.errorMessage = semanticError.duplicateDefine % (sym.name)
                                    self.printError(sym)
                                else:
                                    fieldList.add(sym)  # 添加到字段表
                                self.stepInto("IdMore")  # 步进到下一个字段
                        self.stepInto("FieldDecMore")  # 步进到下一组字段声明
                self.stepInto("END")  # 步进到 END 节点
                recType = RecordType(fieldList=fieldList)  # 创建记录类型
                return recType

    # 分析数组类型
    def arrayType(self):
        self.stepInto("ArrayType")  # 步进到数组类型节点
        self.stepInto("Low")  # 步进到下界
        self.stepInto("INTC")  # 步进到整数常量
        low = self.current.getTokenVal()  # 获取下界值
        self.stepInto("Top")  # 步进到上界
        self.stepInto("INTC")  # 步进到整数常量
        top = self.current.getTokenVal()  # 获取上界值
        self.stepInto("BaseType")  # 步进到基本类型
        self.step()  # 前进一步
        bType = BaseType(kind=self.current.getTokenType())  # 创建基本类型
        typePtr = ArrayType(sz=top - low, low=low, top=top, element=bType)  # 创建数组类型
        if low < 0 or (low >= top):  # 如果数组定义非法（下界负数或大于等于上界）
            self.error = True
            self.errorMessage = semanticError.arrayDefineError
            self.printError()
            return None
        else:
            return typePtr

    # 分析基本类型
    def baseType(self):
        self.stepInto("BaseType")  # 步进到基本类型节点
        self.step()  # 前进一步
        typePtr = BaseType(kind=self.current.getTokenType())  # 创建基本类型
        return typePtr

    # 分析表达式
    def expresion(self):
        """
        返回值：类型和数值
        """
        self.stepInto("Exp")  # 步进到表达式节点
        leftType, leftVal = self.term()  # 分析项
        self.stepInto("OtherTerm")  # 步进到其他项
        expError = False
        while True:
            # 如果到达 OtherTerm 且子节点为空，则退出循环
            if self.current.isTokenType("OtherTerm") and self.current.firstChild().isTokenType("ε"):
                break
            else:
                self.stepInto("Exp")  # 步进到子表达式
                rightType, rightVal = self.expresion()  # 递归分析子表达式
                if not self.assignTypeCheck(leftType, rightType):  # 检查类型兼容性
                    expError = True
                    self.error = True
                    self.errorMessage = semanticError.typeMatchError % (leftType, rightType)
                    self.printError()
                self.stepInto("OtherTerm")  # 步进到下一个其他项
        if expError:  # 如果表达式有错误
            leftType, leftVal = None, None
        return leftType, leftVal

    # 分析项
    def term(self):
        self.stepInto("Term")  # 步进到项节点
        leftType, leftVal = self.factor()  # 分析因子
        self.stepInto("OtherFactor")  # 步进到其他因子
        termError = False
        while True:
            # 如果到达 OtherFactor 且子节点为空，则退出循环
            if self.current.isTokenType("OtherFactor") and self.current.firstChild().isTokenType("ε"):
                break
            else:
                self.stepInto("Term")  # 步进到子项
                rightType, rightVal = self.factor()  # 分析因子
                if not self.assignTypeCheck(leftType, rightType):  # 检查类型兼容性
                    termError = True
                    self.error = True
                    self.errorMessage = semanticError.typeMatchError % (leftType, rightType)
                    self.printError()
                self.stepInto("OtherFactor")  # 步进到下一个其他因子
        if termError:  # 如果项有错误
            leftType, leftVal = (None, None)
        return leftType, leftVal

    # 分析因子
    def factor(self):
        self.stepInto("Factor")  # 步进到因子节点
        self.step()  # 前进一步
        choice = self.current.getTokenType()  # 获取因子类型
        if choice == "(":  # 括号表达式
            return self.expresion()  # 递归分析括号内的表达式
        elif choice == "INTC":  # 整数常量
            return "INTC", self.current.getTokenVal()  # 返回类型和值
        elif choice == "Variable":  # 变量
            return self.variable()  # 分析变量

    # 分析变量
    def variable(self):
        """
        返回值：类型和数值
        """
        self.stepInto("Variable")  # 步进到变量节点
        self.stepInto("ID")  # 步进到标识符
        varName = self.current.getTokenVal()  # 获取变量名
        var = None
        varError = False
        # 从作用域栈逆序查找变量
        for symTable in self.scope[::-1]:
            if varName in symTable:
                var = symTable.get(varName)
                break
        if var == None:  # 如果变量未定义
            varError = True
            self.error = True
            self.errorMessage = semanticError.unDefinedVar
            self.printError()
        self.stepInto("VariMore")  # 步进到变量修饰
        self.step()  # 前进一步
        choice = self.current.getTokenType()  # 获取修饰类型
        if choice == "ε":  # 无修饰
            if varError:
                return None, None
            else:
                return var.typePtr.type, var.value  # 返回变量类型和值
        elif choice == "[":  # 数组索引
            return self.expresion()  # 分析索引表达式
        elif choice == ".":  # 结构体字段
            self.stepInto("FieldVar")  # 步进到字段变量
            self.stepInto("ID")  # 步进到字段名
            fieldName = self.current.getTokenVal()  # 获取字段名
            if var.typePtr.type != "recordType":  # 如果不是结构体类型
                self.error = True
                self.errorMessage = semanticError.typeMatchError % ("recordType", var.typePtr.type)
                self.printError(var)
            else:
                if fieldName not in var.typePtr.fieldList:  # 字段未定义
                    self.error = True
                    self.errorMessage = semanticError.undefinedField
                    self.printError()
                    self.stepInto("FieldVarMore")  # 步进到字段修饰
                    self.step()  # 前进一步
                    if self.current.isTokenType("ε"):
                        return None, None
                    elif self.current.isTokenType("["):
                        self.expresion()
                        return None, None
                else:
                    field = var.typePtr.fieldList.get(fieldName)  # 获取字段符号
                    self.stepInto("FieldVarMore")  # 步进到字段修饰
                    self.step()  # 前进一步
                    if self.current.isTokenType("ε"):  # 无修饰
                        return field.typePtr.type, field.value  # 返回字段类型和值
                    elif self.current.isTokenType("["):  # 数组索引
                        indexType, indexVal = self.expresion()  # 分析索引表达式
                        if indexType not in ["INTEGER", "INTC"]:  # 索引类型必须是整数
                            self.error = True
                            self.errorMessage = semanticError.typeMatchError % ("INTC", indexType)
                            self.printError()
                            return None, None
                        else:
                            return field.typePtr.element.type, None  # 返回数组元素类型

    # 分析条件语句
    def conditionalStm(self):
        self.stepInto("IF")  # 步进到 IF 节点
        condition = self.relExp()  # 分析关系表达式
        self.stepInto("THEN")  # 步进到 THEN 节点
        self.stmList()  # 分析 THEN 部分的语句列表
        self.stepInto("ELSE")  # 步进到 ELSE 节点
        self.stmList()  # 分析 ELSE 部分的语句列表
        self.stepInto("FI")  # 步进到 FI 节点

    # 分析循环语句
    def loopStm(self):
        self.stepInto("WHILE")  # 步进到 WHILE 节点
        condition = self.relExp()  # 分析关系表达式
        self.stepInto("DO")  # 步进到 DO 节点
        self.stmList()  # 分析循环体的语句列表
        self.stepInto("ENDWH")  # 步进到 ENDWH 节点

    # 分析输入语句
    def inputStm(self):
        self.stepInto("InputStm")  # 步进到输入语句节点
        self.stepInto("READ")  # 步进到 READ 节点
        self.stepInto("Invar")  # 步进到输入变量节点
        self.stepInto("ID")  # 步进到标识符
        idName = self.current.getTokenVal()  # 获取变量名
        if idName not in self.symTable:  # 如果变量未定义
            self.error = True
            self.errorMessage = semanticError.unDefinedVar
            self.printError()

    # 分析输出语句
    def outputStm(self):
        self.stepInto("OutputStm")  # 步进到输出语句节点
        self.stepInto("WRITE")  # 步进到 WRITE 节点
        self.expresion()  # 分析输出表达式

    # 分析返回语句
    def returnStm(self):
        self.stepInto("ReturnStm")  # 步进到返回语句节点
        self.stepInto("RETURN")  # 步进到 RETURN 节点
        self.expresion()  # 分析返回表达式

    # 分析关系表达式
    def relExp(self):
        """
        返回值：是否是布尔值
        """
        self.stepInto("RelExp")  # 步进到关系表达式节点
        leftType, leftVal = self.expresion()  # 分析左侧表达式
        self.stepInto("CmpOp")  # 步进到比较运算符
        rightType, rightVal = self.expresion()  # 分析右侧表达式
        if not self.assignTypeCheck(leftType, rightType):  # 检查类型兼容性
            expError = True
            self.error = True
            self.errorMessage = semanticError.typeMatchError % (leftType, rightType)
            self.printError()
            return False
        else:
            return True  # 返回布尔值


# 显示符号表的可视化
def displaySymTable(analyzer, scope=True):
    INT = BaseType(kind="INTEGER")  # 定义 INTEGER 类型
    CHAR = BaseType(kind="CHAR")  # 定义 CHAR 类型
    from graphviz import Digraph  # 导入 graphviz 用于生成图形

    symTable = Digraph(name="symTable", format="png")  # 创建符号表图形对象
    symTable.node_attr["shape"] = "record"  # 设置节点形状为记录
    symTable.graph_attr["rankdir"] = "LR"  # 设置图形方向为左右
    payload = ""  # 存储符号表内容
    element = []  # 存储符号表元素
    symNum = 0  # 符号计数器
    arrayNum = 0  # 数组计数器
    recordNum = 0  # 记录计数器
    procNum = 0  # 过程计数器
    hasInteger = False  # 是否已定义 INTEGER 类型
    hasChar = False  # 是否已定义 CHAR 类型
    length = len(analyzer.scope)  # 作用域栈长度
    for level in range(length):  # 遍历作用域栈
        currentSymTable = analyzer.scope[level]  # 获取当前作用域的符号表
        if level != 0:  # 如果不是全局作用域
            currentSymTable.pop(0)  # 移除过程符号（已在上一级处理）
        for sym in currentSymTable:  # 遍历符号表中的符号
            if sym.typePtr is None and sym.decKind == "procDec":  # 如果是过程声明
                paramList = sym.param  # 获取参数表
                element.append("{%s|procDec|<paramList%d> paramList}" % (sym.name, procNum))  # 添加过程节点
                symTable.edge("element:paramList%d" % procNum, "paramList%d" % procNum)  # 连接参数表
                params = []  # 存储参数信息
                for param in paramList:  # 遍历参数
                    paramType = param.typePtr.type  # 获取参数类型
                    if paramType == "INTEGER":  # INTEGER 类型
                        if not hasInteger:
                            symTable.node(name="INTEGER", label="{%d|%s}" % (INT.sz, INT.type))
                            hasInteger = True
                    elif paramType == "CHAR":  # CHAR 类型
                        if not hasChar:
                            symTable.node(name="CHAR", label="{%d|%s}" % (CHAR.sz, CHAR.type))
                            hasChar = True
                    elif paramType == "recordType":  # 记录类型
                        paramType = "record%d" % recordNum
                        record = param.typePtr
                        symTable.node(name=paramType, label="{record|<fieldList%d> fieldList}" % recordNum)
                        symTable.edge("record%d:fieldList%d" % (recordNum, recordNum), "fieldList%d" % recordNum)
                        fieldList = record.fieldList  # 获取字段表
                        fields = []  # 存储字段信息
                        for field in fieldList:  # 遍历字段
                            fieldType = field.typePtr.type  # 获取字段类型
                            if fieldType == "INTEGER":
                                if not hasInteger:
                                    symTable.node(name="INTEGER", label="{%d|%s}" % (INT.sz, INT.type))
                                    hasInteger = True
                            elif fieldType == "CHAR":
                                if not hasChar:
                                    symTable.node(name="CHAR", label="{%d|%s}" % (CHAR.sz, CHAR.type))
                                    hasChar = True
                            elif fieldType == "arrayType":  # 数组类型
                                fieldType = "array%d" % arrayNum
                                array = field.typePtr
                                symTable.node(
                                    name="array%d" % arrayNum,
                                    label="{array|sz:%d|low:%d|top:%d|<elementPtr%d> type}" % (
                                        array.sz, array.low, array.top, arrayNum),
                                )
                                elementType = array.element.type
                                if elementType == "CHAR" and not hasChar:
                                    symTable.node(name="CHAR", label="{%d|%s}" % (CHAR.sz, CHAR.type))
                                    hasChar = True
                                elif elementType == "INTEGER" and not hasInteger:
                                    symTable.node(name="INTEGER", label="{%d|%s}" % (INT.sz, INT.type))
                                    hasInteger = True
                                symTable.edge("array%d:elementPtr%d" % (arrayNum, arrayNum), array.element.type)
                                arrayNum += 1
                            fields.append("{%s|%s|<typePtr%d>type}" % (field.name, field.decKind, symNum))
                            symTable.edge("fieldList%d:typePtr%d" % (recordNum, symNum), fieldType)
                            symNum += 1
                        symTable.node(name="fieldList%d" % recordNum, label="|".join(fields))
                        recordNum += 1
                    elif paramType == "arrayType":  # 数组类型
                        paramType = "array%d" % arrayNum
                        array = param.typePtr
                        symTable.node(
                            name="array%d" % arrayNum,
                            label="{array|sz:%d|low:%d|top:%d|<elementPtr%d> type}" % (
                                array.sz, array.low, array.top, arrayNum),
                        )
                        elementType = array.element.type
                        if elementType == "CHAR" and not hasChar:
                            symTable.node(name="CHAR", label="{%d|%s}" % (CHAR.sz, CHAR.type))
                            hasChar = True
                        elif elementType == "INTEGER" and not hasInteger:
                            symTable.node(name="INTEGER", label="{%d|%s}" % (INT.sz, INT.type))
                            hasInteger = True
                        symTable.edge("array%d:elementPtr%d" % (arrayNum, arrayNum), array.element.type)
                        arrayNum += 1
                    else:
                        continue
                    params.append("{%s|%s|<typePtr%d>type}" % (param.name, param.decKind, symNum))
                    symTable.edge("paramList%d:typePtr%d" % (procNum, symNum), paramType)
                    symNum += 1
                symTable.node("paramList%d" % procNum, label="|".join(params))
                procNum += 1
                continue
            symType = sym.typePtr.type  # 获取符号类型
            if symType == "INTEGER":
                if not hasInteger:
                    symTable.node(name="INTEGER", label="{%d|%s}" % (INT.sz, INT.type))
                    hasInteger = True
            elif symType == "CHAR":
                if not hasChar:
                    symTable.node(name="CHAR", label="{%d|%s}" % (CHAR.sz, CHAR.type))
                    hasChar = True
            elif symType == "recordType":  # 记录类型
                symType = "record%d" % recordNum
                record = sym.typePtr
                symTable.node(name=symType, label="{record|<fieldList%d> fieldList}" % recordNum)
                symTable.edge("record%d:fieldList%d" % (recordNum, recordNum), "fieldList%d" % recordNum)
                fieldList = record.fieldList
                fields = []
                for field in fieldList:
                    fieldType = field.typePtr.type
                    if fieldType == "INTEGER":
                        if not hasInteger:
                            symTable.node(name="INTEGER", label="{%d|%s}" % (INT.sz, INT.type))
                            hasInteger = True
                    elif fieldType == "CHAR":
                        if not hasChar:
                            symTable.node(name="CHAR", label="{%d|%s}" % (CHAR.sz, CHAR.type))
                            hasChar = True
                    elif fieldType == "arrayType":
                        fieldType = "array%d" % arrayNum
                        array = field.typePtr
                        symTable.node(
                            name="array%d" % arrayNum,
                            label="{array|sz:%d|low:%d|top:%d|<elementPtr%d> type}" % (
                                array.sz, array.low, array.top, arrayNum),
                        )
                        elementType = array.element.type
                        if elementType == "CHAR" and not hasChar:
                            symTable.node(name="CHAR", label="{%d|%s}" % (CHAR.sz, CHAR.type))
                            hasChar = True
                        elif elementType == "INTEGER" and not hasInteger:
                            symTable.node(name="INTEGER", label="{%d|%s}" % (INT.sz, INT.type))
                            hasInteger = True
                        symTable.edge("array%d:elementPtr%d" % (arrayNum, arrayNum), array.element.type)
                        arrayNum += 1
                    fields.append("{%s|%s|<typePtr%d>type}" % (field.name, field.decKind, symNum))
                    symTable.edge("fieldList%d:typePtr%d" % (recordNum, symNum), fieldType)
                    symNum += 1
                symTable.node(name="fieldList%d" % recordNum, label="|".join(fields))
                recordNum += 1
            elif symType == "arrayType":  # 数组类型
                symType = "array%d" % arrayNum
                array = sym.typePtr
                symTable.node(
                    name="array%d" % arrayNum,
                    label="{array|sz:%d|low:%d|top:%d|<elementPtr%d> type}" % (
                        array.sz, array.low, array.top, arrayNum),
                )
                elementType = array.element.type
                if elementType == "CHAR" and not hasChar:
                    symTable.node(name="CHAR", label="{%d|%s}" % (CHAR.sz, CHAR.type))
                    hasChar = True
                elif elementType == "INTEGER" and not hasInteger:
                    symTable.node(name="INTEGER", label="{%d|%s}" % (INT.sz, INT.type))
                    hasInteger = True
                symTable.edge("array%d:elementPtr%d" % (arrayNum, arrayNum), array.element.type)
                arrayNum += 1
            else:
                continue
            element.append("{%s|%s|<typePtr%d>type}" % (sym.name, sym.decKind, symNum))
            symTable.edge("element:typePtr%d" % symNum, symType)
            symNum += 1
        if not scope:  # 如果不处理多作用域，则只处理全局作用域后退出
            break

    payload = "|".join(element)  # 将所有元素连接成字符串
    symTable.node(name="element", label=payload)  # 创建主节点
    symTable.render("symTable", view=True)  # 渲染并显示符号表图形


# 主程序入口
if __name__ == "__main__":
    tokens = open("../Lexer/source-Lexer.txt", "r").readlines()  # 读取词法分析结果
    index = 0
    root = generateAST(tokens)  # 生成抽象语法树
    tokens = [eval(token) for token in tokens]  # 将 token 字符串转换为 Python 对象

    import json

    astfile = open("../Parser/source-ast_json.txt", "w")  # 打开 AST 输出文件
    json.dump(root, astfile, cls=AstNodeEncoder)  # 将 AST 序列化为 JSON

    astfile.close()  # 关闭文件
    analyzer = Analyzer(tokens, root)  # 创建语义分析器实例
    analyzer.analyze()  # 执行语义分析
    displaySymTable(analyzer, True)  # 显示符号表图形
